#include <string.h>

#include "net_context.h"
#include "framebuf_mgr.h"

#define MEMBER_OFFSET(type, mbr) ((uint32_t) (uintptr_t)(&((type*)0)->mbr))

using namespace usnet;

std::shared_ptr<FrameBufMgr> FrameBufMgr::MakeShared(
    EFResource* ef_res,
    const uint32_t nr_rx_fb,
    const uint32_t nr_tx_fb)
{
    auto ctx = NetContext::GetContext();
    std::shared_ptr<FrameBufMgr> fb_mgr { new(std::nothrow) FrameBufMgr { ef_res, nr_rx_fb, nr_tx_fb } };
    if (fb_mgr == nullptr) {
        LOG_ERROR(ctx, "failed to new FrameBufMgr instance");
        return nullptr;
    }
    if (fb_mgr->InitFrameBuf() == false) {
        LOG_ERROR(ctx, "failed to init FrameBuf");
        return nullptr;
    }
    return fb_mgr;
}

bool FrameBufMgr::InitFrameBuf()
{
    auto ctx = NetContext::GetContext();
    const int bytes = (nr_rx_fb_ + nr_tx_fb_) * fb_size_;
    if (posix_memalign(&mem_ptr_, memalign_, bytes) != 0) {
        LOG_ERROR(ctx, "posix_memalign() failed");
        return false;
    }
    memset(mem_ptr_, 0, bytes);
    if (ef_memreg_alloc(
        &ef_res_->memreg_, ef_res_->handle_,
        &ef_res_->pd_, ef_res_->handle_,
        mem_ptr_, bytes) != 0) {
        LOG_ERROR(ctx, "ef_memreg_alloc() failed");
        return false;
    }
    fb_tab_ = new(std::nothrow) FrameBuf*[nr_rx_fb_ + nr_tx_fb_];
    if (fb_tab_ == nullptr) {
        LOG_ERROR(ctx, "failed to new FrameBuf pointer table");
        return false;
    }
    const int mr_offset = MEMBER_OFFSET(FrameBuf, mem_addr_);
    int fb_offset { 0 };
    for (uint32_t k = 0; k < nr_rx_fb_; ++k) {
        FrameBuf* fb = (FrameBuf*) (((char*) mem_ptr_) + fb_offset);
        fb->id_ = k;
        fb->link_.next_ = &fb->link_;
        fb->link_.prev_ = &fb->link_;
        fb->dma_addr_ = ef_memreg_dma_addr(&ef_res_->memreg_, fb_offset + mr_offset);
        fb_tab_[fb->id_] = fb;
        if (ef_vi_receive_init(&ef_res_->vi_, fb->dma_addr_, fb->id_) != 0) {
            LOG_ERROR(ctx, "ef_vi_receive_post() failed");
            return false;
        }
        ef_vi_receive_push(&ef_res_->vi_);
        rx_fb_free_.TailInsert(fb);
        fb_offset += fb_size_;
    }
    for (uint32_t k = nr_rx_fb_; k < (nr_rx_fb_ + nr_tx_fb_); ++k) {
        FrameBuf* fb = (FrameBuf*) (((char*) mem_ptr_) + fb_offset);
        fb->id_ = k;
        fb->link_.next_ = &fb->link_;
        fb->link_.prev_ = &fb->link_;
        fb->dma_addr_ = ef_memreg_dma_addr(&ef_res_->memreg_, fb_offset + mr_offset);
        fb_tab_[fb->id_] = fb;
        tx_fb_free_.TailInsert(fb);
        fb_offset += fb_size_;
    }
    return true;
}
